/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the  "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id$
 */
package wx.xml.xalan.xalan.templates;

import org.xml.sax.SAXException;

import javax.xml.transform.TransformerException;

import wx.xml.xalan.xalan.extensions.ExtensionHandler;
import wx.xml.xalan.xalan.extensions.ExtensionsTable;
import wx.xml.xalan.xalan.res.XSLMessages;
import wx.xml.xalan.xalan.res.XSLTErrorResources;
import wx.xml.xalan.xalan.transformer.TransformerImpl;
import wx.xml.xalan.xpath.XPathContext;

/**
 * Implement an extension element.
 *
 * @xsl.usage advanced
 * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
 */
public class ElemExtensionCall extends ElemLiteralResult {
    static final long serialVersionUID = 3171339708500216920L;

    /**
     * The Namespace URI for this extension call element.
     *
     * @serial
     */
    String m_extns;

    /**
     * Language used by extension.
     *
     * @serial
     */
    String m_lang;

    /**
     * URL pointing to extension.
     *
     * @serial
     */
    String m_srcURL;

    /**
     * Source for script.
     *
     * @serial
     */
    String m_scriptSrc;

    /**
     * Declaration for Extension element.
     *
     * @serial
     */
    ElemExtensionDecl m_decl = null;

    /**
     * Get an int constant identifying the type of element.
     *
     * @return The token ID for this element
     * @see wx.xml.xalan.xalan.templates.Constants
     */
    public int getXSLToken() {
        return Constants.ELEMNAME_EXTENSIONCALL;
    }

    /**
     * Return the node name.
     *
     * @return The element's name
     */

    // public String getNodeName()
    // {
    // TODO: Need prefix.
    // return localPart;
    // }

    /**
     * This function is called after everything else has been
     * recomposed, and allows the template to set remaining
     * values that may be based on some other property that
     * depends on recomposition.
     */
    public void compose(StylesheetRoot sroot) throws TransformerException {
        super.compose(sroot);
        m_extns = this.getNamespace();
        m_decl = getElemExtensionDecl(sroot, m_extns);
        // Register the extension namespace if the extension does not have
        // an ElemExtensionDecl ("component").
        if (m_decl == null)
            sroot.getExtensionNamespacesManager().registerExtension(m_extns);
    }

    /**
     * Return the ElemExtensionDecl for this extension element
     *
     * @param stylesheet Stylesheet root associated with this extension element
     * @param namespace  Namespace associated with this extension element
     * @return the ElemExtensionDecl for this extension element.
     */
    private ElemExtensionDecl getElemExtensionDecl(StylesheetRoot stylesheet,
                                                   String namespace) {

        ElemExtensionDecl decl = null;
        int               n    = stylesheet.getGlobalImportCount();

        for (int i = 0; i < n; i++) {
            Stylesheet imported = stylesheet.getGlobalImport(i);

            for (ElemTemplateElement child = imported.getFirstChildElem();
                 child != null; child = child.getNextSiblingElem()) {
                if (Constants.ELEMNAME_EXTENSIONDECL == child.getXSLToken()) {
                    decl = (ElemExtensionDecl) child;

                    String prefix        = decl.getPrefix();
                    String declNamespace = child.getNamespaceForPrefix(prefix);

                    if (namespace.equals(declNamespace)) {
                        return decl;
                    }
                }
            }
        }

        return null;
    }

    /**
     * Execute the fallbacks when an extension is not available.
     *
     * @param transformer non-null reference to the the current transform-time state.
     * @throws TransformerException
     */
    private void executeFallbacks(
        TransformerImpl transformer)
        throws TransformerException {
        for (ElemTemplateElement child = m_firstChild; child != null;
             child = child.m_nextSibling) {
            if (child.getXSLToken() == Constants.ELEMNAME_FALLBACK) {
                try {
                    transformer.pushElemTemplateElement(child);
                    ((ElemFallback) child).executeFallback(transformer);
                } finally {
                    transformer.popElemTemplateElement();
                }
            }
        }

    }

    /**
     * Return true if this extension element has a <xsl:fallback> child element.
     *
     * @return true if this extension element has a <xsl:fallback> child element.
     */
    private boolean hasFallbackChildren() {
        for (ElemTemplateElement child = m_firstChild; child != null;
             child = child.m_nextSibling) {
            if (child.getXSLToken() == Constants.ELEMNAME_FALLBACK)
                return true;
        }

        return false;
    }


    /**
     * Execute an extension.
     *
     * @param transformer non-null reference to the the current transform-time state.
     * @throws TransformerException
     */
    public void execute(TransformerImpl transformer)
        throws TransformerException {
        if (transformer.getStylesheet().isSecureProcessing())
            throw new TransformerException(
                XSLMessages.createMessage(
                    XSLTErrorResources.ER_EXTENSION_ELEMENT_NOT_ALLOWED_IN_SECURE_PROCESSING,
                    new Object[]{getRawName()}));

        if (transformer.getDebug())
            transformer.getTraceManager().fireTraceEvent(this);
        try {
            transformer.getResultTreeHandler().flushPending();

            ExtensionsTable  etable = transformer.getExtensionsTable();
            ExtensionHandler nsh    = etable.get(m_extns);

            if (null == nsh) {
                if (hasFallbackChildren()) {
                    executeFallbacks(transformer);
                } else {
                    TransformerException te = new TransformerException(XSLMessages.createMessage(
                        XSLTErrorResources.ER_CALL_TO_EXT_FAILED, new Object[]{getNodeName()}));
                    transformer.getErrorListener().fatalError(te);
                }

                return;
            }

            try {
                nsh.processElement(this.getLocalName(), this, transformer,
                    getStylesheet(), this);
            } catch (Exception e) {

                if (hasFallbackChildren())
                    executeFallbacks(transformer);
                else {
                    if (e instanceof TransformerException) {
                        TransformerException te = (TransformerException) e;
                        if (null == te.getLocator())
                            te.setLocator(this);

                        transformer.getErrorListener().fatalError(te);
                    } else if (e instanceof RuntimeException) {
                        transformer.getErrorListener().fatalError(new TransformerException(e));
                    } else {
                        transformer.getErrorListener().warning(new TransformerException(e));
                    }
                }
            }
        } catch (TransformerException e) {
            transformer.getErrorListener().fatalError(e);
        } catch (SAXException se) {
            throw new TransformerException(se);
        }
        if (transformer.getDebug())
            transformer.getTraceManager().fireTraceEndEvent(this);
    }

    /**
     * Return the value of the attribute interpreted as an Attribute
     * Value Template (in other words, you can use curly expressions
     * such as href="http://{website}".
     *
     * @param rawName     Raw name of the attribute to get
     * @param sourceNode  non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
     * @param transformer non-null reference to the the current transform-time state.
     * @return the value of the attribute
     * @throws TransformerException
     */
    public String getAttribute(
        String rawName, org.w3c.dom.Node sourceNode, TransformerImpl transformer)
        throws TransformerException {

        AVT avt = getLiteralResultAttribute(rawName);

        if ((null != avt) && avt.getRawName().equals(rawName)) {
            XPathContext xctxt = transformer.getXPathContext();

            return avt.evaluate(xctxt,
                xctxt.getDTMHandleFromNode(sourceNode),
                this);
        }

        return null;
    }

    /**
     * Accept a visitor and call the appropriate method
     * for this class.
     *
     * @param visitor The visitor whose appropriate method will be called.
     * @return true if the children of the object should be visited.
     */
    protected boolean accept(XSLTVisitor visitor) {
        return visitor.visitExtensionElement(this);
    }


}
